module Dcache(
    input reset,
    input clk,
    input data_r_valid,
    input data_w_valid,

    input [63:0] addr,
    input [1:0] control,
    input [7:0] mask,

    output reg [63:0] wb_addr,
    output reg [63:0] wb_data,
    output reg data_w_ena,
    output reg data_r_ena,
    output reg [63:0] r_addr,
    output reg [63:0] data_o,

    input [63:0] r_data,
    input [63:0] w_data,
    output reg stall
);

    parameter IDEL = 3'b000;
    parameter write_back = 3'b001;
    parameter refill = 3'b010;
    parameter write = 3'b011;
    parameter read = 3'b100;

    reg [2:0] state;
    reg [64:0] data_arrays_0[0:63];
    reg [64:0] data_arrays_1[0:63];
    reg [54:0] tag_arrays_0[0:63];
    reg [54:0] tag_arrays_1[0:63];
    reg age_tag[0:63];

    wire hit;
    wire hit0;
    wire hit1;
    wire write_way;

    assign hit0 = tag_arrays_0[addr[9:3]][54] == 1'b1 && tag_arrays_0[addr[9:3]][53:0] == addr[63:10];
    assign hit1 = tag_arrays_1[addr[9:3]][54] == 1'b1 && tag_arrays_1[addr[9:3]][53:0] == addr[63:10];
    assign hit = hit0 | hit1;
    assign write_way = age_tag[addr[9:3]];

    always @(posedge clk) begin
        if(reset) begin
            state <= IDEL;
            stall <= 1'b0;
            genvar i;
	        generate
		        for (i = 0; i < 64; i = i + 1) begin
		        	tag_arrays_0[i] <= 55'd0;
                    tag_arrays_1[i] <= 55'd0;
                    age_tag[i] <= 1'b0;
                    data_arrays_0[i] <= 65'd0;
                    data_arrays_1[i] <= 65'd0; 
		        end
	        endgenerate
        end else begin
            case(state)
                IDEL : begin
                    if(~hit && ((write_way == 1'b0 && data_arrays_0[addr[9:3]][0] == 1'b1) || (write_way == 1'b1 && data_arrays_1[addr[9:3]][0] == 1'b1))) begin
                        state <= write_back;
                        data_w_ena <= 1'b1;
                        wb_data <= (write_way == 1'b0) ? data_arrays_0[addr[9:3]][64:1] : data_arrays_1[addr[9:3]][64:1];
                        wb_addr <= (write_way == 1'b0) ? {tag_arrays_0[53:0], addr[9:3], 3'b000} : {tag_arrays_1[53:0], addr[9:3], 3'b000};
                    end else if(~hit && ~((write_way == 1'b0 && data_arrays_0[addr[9:3]][0] == 1'b1) || (write_way == 1'b1 && data_arrays_1[addr[9:3]][0] == 1'b1))) begin
                        state <= refill_send;
                        r_addr <= {addr[63:3], 3'b000};
                        data_r_ena <= 1'b1;
                    end
                    stall <= 1'b1;
                end
                write_back : begin
                    if(data_w_valid) begin
                        state <= refill_send;
                        r_addr <= {addr[63:3], 3'b000};
                        data_r_ena <= 1'b1;
                        data_w_ena <= 1'b0;
                    end
                    stall <= 1'b1;
                end
                refill : begin
                    if(data_r_valid) begin
                        data_r_ena <= 1'b0;
                        if(control == 2'b01) begin
                            state <= write;
                        end else if(control == 2'b10) begin
                            state <= read;
                        end
                        if(write_way) begin
                            data_arrays_0[addr[9:3]][64:1] <= r_data;
                            tag_arrays_0[addr[9:3]][54] <= 1'b1;
                        end else begin
                            data_arrays_1[addr[9:3]][64:1] <= r_data;
                            tag_arrays_1[addr[9:3]][54] <= 1'b1;
                        end
                    end
                    stall <= 1'b1;
                end
                write : begin
                    state <= IDEL;
                    if(hit) begin
                        if(hit0) begin
                            age_tag[addr[9:3]] <= 1'b1;
                            data_arrays_0[addr[9:3]][64:1] <= w_data;
                            data_arrays_0[addr[9:3]][0] <= 1'b1;
                            tag_arrays_0[addr[9:3]][54] <= 1'b1;
                        end else begin
                            age_tag[addr[9:3]] <= 1'b0;
                            data_arrays_1[addr[9:3]][64:1] <= w_data;
                            data_arrays_1[addr[9:3]][0] <= 1'b1;
                            tag_arrays_1[addr[9:3]][54] <= 1'b1;
                        end
                    end else begin
                        if(write_way) begin
                            data_arrays_0[addr[9:3]][64:1] <= w_data;
                            data_arrays_0[addr[9:3]][0] <= 1'b1;
                            tag_arrays_0[addr[9:3]][54] <= 1'b1;
                            tag_arrays_0[addr[9:3]][53:0] <= addr[63:10];
                        end else begin
                            data_arrays_1[addr[9:3]][64:1] <= w_data;
                            data_arrays_1[addr[9:3]][0] <= 1'b1;
                            tag_arrays_1[addr[9:3]][54] <= 1'b1;
                            tag_arrays_1[addr[9:3]][53:0] <= addr[63:10];
                        end
                    end
                    stall <= 1'b0;
                end
                read : begin
                    state <= IDEL;
                    if(hit) begin
                        if(hit0) begin
                            age_tag[addr[9:3]] <= 1'b1;
                            data_o <= data_arrays_0[addr[9:3]][64:1];
                            data_arrays_0[addr[9:3]][0] <= 1'b0;
                        end else begin
                            age_tag[addr[9:3]] <= 1'b0;
                            data_o <= data_arrays_1[addr[9:3]][64:1];
                            data_arrays_1[addr[9:3]][0] <= 1'b0;
                        end
                    end else begin
                        if(write_way) begin
                            data_o <= data_arrays_0[addr[9:3]][64:1];
                            data_arrays_0[addr[9:3]][0] <= 1'b0;
                            tag_arrays_0[addr[9:3]][53:0] <= addr[63:10];
                        end else begin
                            data_o <= data_arrays_1[addr[9:3]][64:1];
                            data_arrays_1[addr[9:3]][0] <= 1'b0;
                            tag_arrays_1[addr[9:3]][53:0] <= addr[63:10];
                        end
                    end
                    stall <= 1'b0;
                end
            endcase
        end
    end

endmodule